home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
rpg
/
crossfir.92
/
crossfir
/
crossfire-0.92.5
/
server
/
shop.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-24
|
18KB
|
606 lines
/*
* static char *rcsid_shop_c =
* "$Id: shop.c,v 1.32 1996/01/02 11:49:55 master Exp $";
*/
/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 1994 Mark Wedel
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author can be reached via e-mail to master@rahul.net
*/
#include <global.h>
#include <spells.h>
#include <skills.h>
#include <living.h>
#include <newclient.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#define NUM_COINS 3 /* number of coin types */
static char *coins[] = {"platinacoin", "goldcoin", "silvercoin", NULL};
/* Added F_TRUE flag to define.h to mean that the price should not
* be adjusted by players charisma. With F_TRUE, it returns the amount
* that the item is worth, if it was sold, but unadjusted by charisma.
* This is needed for alchemy, to to determine what value of gold nuggets
* should be given (the gold nuggets, when sold, will have the adjustment
* by charisma done at that time). NULL could have been passed as the
* who parameter, but then the adjustment for expensive items (>10000)
* would not be done.
*
* CF 0.91.4 - This function got changed around a bit. Now the
* number of object is multiplied by the value early on. This fixes problems
* with items worth very little. What happened before is that various
* divisions took place, the value got rounded to 0 (Being an int), and
* thus remained 0.
*
* Mark Wedel (master@rahul.net)
*/
int query_cost(object *tmp, object *who, int flag) {
int val;
int number; /* used to better calculate value */
int charisma;
if (tmp->type==MONEY) return (tmp->nrof * tmp->value);
if (tmp->type==GEM) {
if (flag==F_TRUE) return (tmp->nrof * tmp->value);
if (flag==F_BUY) return (1.03 * tmp->nrof * tmp->value);
if (flag==F_SELL) return (0.97 * tmp->nrof * tmp->value);
LOG(llevError,"Query_cost: Gem type with unknown flag : %d\n", flag);
return 0;
}
number = tmp->nrof;
if (number==0) number=1;
if (QUERY_FLAG(tmp, FLAG_IDENTIFIED) || !need_identify(tmp)) {
if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
return 0;
else
val=tmp->value * number;
}
/* This area deals with objects that are not identified, but can be */
else {
if (tmp->arch != NULL) {
if (flag == F_BUY) {
LOG(llevError, "Asking for buy-value of unidentified object.");
val = tmp->arch->clone.value * 50 * number;
}
else /* Trying to sell something, or get true value */
if (tmp->type == POTION)
val = number * 1000; /* Don't want to give anything away */
else
val = number * tmp->arch->clone.value / 3;
} else { /* No archetype with this object */
LOG(llevDebug,"In sell item: Have object with no archetype: %s\n", tmp->name);
if (flag == F_BUY) {
LOG(llevError, "Asking for buy-value of unidentified object without arch.");
val = number * tmp->value * 10;
}
else
val = number * tmp->value / 5;
}
}
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && QUERY_FLAG(tmp, FLAG_BEEN_APPLIED))
val = val * 2 / 3;
/* If the item has been applied or identifed or does not need to be
* identified, AND the object is magical and the archetype is non
* magical, then change values accordingly. The tmp->arch==NULL is
* really just a check to prevent core dumps for when it checks
* tmp->arch->clone.magic for any magic. The check for archetype
* magic is to not give extra money for archetypes that are by
* default magical. This is because the archetype value should have
* already figured in that value.
*/
if((QUERY_FLAG(tmp, FLAG_IDENTIFIED)||!need_identify(tmp)||
QUERY_FLAG(tmp, FLAG_BEEN_APPLIED)) &&
tmp->magic&&(tmp->arch==NULL||!tmp->arch->clone.magic)) {
if(tmp->magic>0)
val*=(3*tmp->magic*tmp->magic*tmp->magic);
else
/* Note that tmp->magic is negative, so that this
* will actually be something like val /=2, /=3, etc.
*/
val/=(1-tmp->magic);
}
if (flag==F_BUY)
val*=6; /* For charisma 25, the price is now x1.5 */
if (tmp->type==WAND) {
if (QUERY_FLAG(tmp, FLAG_IDENTIFIED) || !need_identify(tmp))
val=(val*tmp->stats.food)/spells[tmp->stats.sp].charges;
else
val/=3;
}
if (flag==F_TRUE || flag==F_SELL) {
if (val/number>10000) {
val=8000+isqrt((int)val/number)*20;
val *= number;
}
}
/* this modification is for merchant skill
* now players with readied merchant skill get
* more Cha up to the limit of modified Cha = 30.
* -b.t. thomas@nomad.astro.psu.edu
*/
if (who!=NULL && who->type==PLAYER) {
charisma = who->stats.Cha; /* used for SK_BARGAINING modification */
if(who->contr->shoottype == range_skill && who->chosen_skill)
if(who->chosen_skill->stats.sp == SK_BARGAINING) {
charisma = who->stats.Cha + (who->level+2)/3;
if(charisma>30) charisma = 30;
}
if(flag==F_BUY)
val=(val*(100-cha_bonus[charisma]))/100;
else if (flag==F_SELL)
val=(val*100)/(100-cha_bonus[charisma]);
}
if(val<0)
val=1000000;
return (int)val;
}
/* Find the coin type that is worth more the 'c'. Starts at the
* cointype placement.
*/
static archetype *find_next_coin(int c, int *cointype) {
archetype *coin;
do {
if (coins[*cointype]==NULL) return NULL;
coin = find_archetype(coins[*cointype]);
if (coin == NULL)
return NULL;
*cointype += 1;
} while (coin->clone.value > c);
return coin;
}
/* This returns a string of how much somethign is worth based on
* an integer being passed.
*/
char *cost_string_from_value(int cost)
{
static char buf[MAX_BUF];
archetype *coin, *next_coin;
char *endbuf;
int num, cointype = 0;
coin = find_next_coin(cost, &cointype);
if (coin == NULL)
return "nothing";
num = cost / coin->clone.value;
cost -= num * coin->clone.value;
if (num == 1)
sprintf(buf, "1 %s", coin->clone.name);
else
sprintf(buf, "%d %ss", num, coin->clone.name);
next_coin = find_next_coin(cost, &cointype);
if (next_coin == NULL)
return buf;
do {
endbuf = buf + strlen(buf);
coin = next_coin;
num = cost / coin->clone.value;
cost -= num * coin->clone.value;
if (cost == 0)
next_coin = NULL;
else
next_coin = find_next_coin(cost, &cointype);
if (next_coin) {
/* There will be at least one more string to add to the list,
* use a comma.
*/
strcat(endbuf, ", "); endbuf += 2;
} else {
strcat(endbuf, " and "); endbuf += 5;
}
if (num == 1)
sprintf(endbuf, "1 %s", coin->clone.name);
else
sprintf(endbuf, "%d %ss", num, coin->clone.name);
} while (next_coin);
return buf;
}
char *query_cost_string(object *tmp,object *who,int flag) {
return cost_string_from_value(query_cost(tmp,who,flag));
}
/* This function finds out how much money the player is carrying,
* and returns that value
*/
int query_money(object *op) {
object *tmp;
int total=0;
if (op->type!=PLAYER) {
LOG(llevError, "Query money called with non player");
return 0;
}
for (tmp = op->inv; tmp; tmp= tmp->below) {
if (tmp->type==MONEY)
total += tmp->nrof * tmp->value;
}
return total;
}
/* This pays for the item, and takes the proper amount of money off
* the player.
* CF 0.91.4 - this function is mostly redone in order to fix a bug
* with weight not be subtracted properly. We now remove and
* insert the coin objects - this should update the weight
* appropriately
*/
int pay_for_item(object *op,object *pl) {
int item_cost=query_cost(op,pl,F_BUY),i, count;
int to_pay;
object *tmp, *coin_objs[NUM_COINS];
archetype *at;
if (item_cost==0) return 1;
if(item_cost>query_money(pl))
return 0;
for (i=0; i<NUM_COINS; i++) {
coin_objs[i] = NULL;
}
pl->contr->freeze_inv=1;
/* This hunk should remove all the money objects from the player */
while ((tmp=present_in_ob(MONEY, pl))!=NULL) {
for (i=0; i<NUM_COINS; i++) {
if (!strcmp(coins[NUM_COINS-1-i], tmp->arch->name)) {
/* This should not happen, but if it does, just
* merge the two.
*/
if (coin_objs[i]!=NULL) {
LOG(llevError,"%s has two money entries of (%s)\n",
pl->name, coins[NUM_COINS-1-i]);
remove_ob(tmp);
coin_objs[i]->nrof += tmp->nrof;
if (pl->contr->eric_server)
esrv_del_item(pl->contr->eric_server, tmp->count);
free_object(tmp);
}
else {
remove_ob(tmp);
if (pl->contr->eric_server)
esrv_del_item(pl->contr->eric_server, tmp->count);
coin_objs[i] = tmp;
}
break;
}
}
if (i==NUM_COINS)
LOG(llevError,"in pay_for_item: Did not find string match for %s\n", tmp->arch->name);
}
/* Fill in any gaps in the coin_objs array - needed to make change. */
/* Note that the coin_objs array goes from least value to greatest value */
for (i=0; i<NUM_COINS; i++)
if (coin_objs[i]==NULL) {
at = find_archetype(coins[NUM_COINS-1-i]);
if (at==NULL) LOG(llevError, "Could not find %s archetype", coins[NUM_COINS-1-i]);
coin_objs[i] =get_object();
copy_object(&at->clone, coin_objs[i]);
coin_objs[i]->nrof = 0;
}
to_pay = item_cost;
for (i=0; i<NUM_COINS; i++) {
int num_coins;
if (coin_objs[i]->nrof*coin_objs[i]->value> to_pay) {
num_coins = to_pay / coin_objs[i]->value;
if (num_coins*coin_objs[i]->value< to_pay) num_coins++;
}
else
num_coins = coin_objs[i]->nrof;
to_pay -= num_coins * coin_objs[i]->value;
coin_objs[i]->nrof -= num_coins;
/* Now start making change. Start at the coin value
* below the one we just did, and work down to
* the lowest value.
*/
count=i-1;
while (to_pay<0 && count>=0) {
num_coins = -to_pay/ coin_objs[count]->value;
coin_objs[count]->nrof += num_coins;
to_pay += num_coins * coin_objs[count]->value;
count--;
}
}
for (i=0; i<NUM_COINS; i++) {
if (coin_objs[i]->nrof) {
object *tmp = insert_ob_in_ob(coin_objs[i], pl);
if (pl->contr->eric_server > 0)
esrv_send_item(pl, tmp);
} else
free_object(coin_objs[i]);
}
if(QUERY_FLAG(pl,FLAG_WAS_WIZ))
SET_FLAG(op, FLAG_WAS_WIZ);
pl->contr->freeze_inv=0;
fix_player(pl);
return 1;
}
/* Eneq(@csd.uu.se): Better get_payment, descends containers looking for
unpaid items. get_payment is now used as a link. To make it simple
we need the player-object here. */
int get_payment2 (object *pl, object *op) {
char buf[MAX_BUF];
int ret=1;
if (op!=NULL&&op->inv)
ret = get_payment2(pl, op->inv);
if (!ret)
return 0;
if (op!=NULL&&op->below)
ret = get_payment2 (pl, op->below);
if (!ret)
return 0;
if(op!=NULL&&QUERY_FLAG(op,FLAG_UNPAID)) {
strncpy(buf,query_cost_string(op,pl,F_BUY),MAX_BUF);
if(!pay_for_item(op,pl)) {
int i=query_cost(op,pl,F_BUY) - query_money(pl);
CLEAR_FLAG(op, FLAG_UNPAID);
new_draw_info_format(NDI_UNIQUE, 0, pl,
"You lack %s to buy %s.", cost_string_from_value(i),
query_name(op));
SET_FLAG(op, FLAG_UNPAID);
return 0;
} else {
object *tmp;
long c = op->count;
CLEAR_FLAG(op, FLAG_UNPAID);
new_draw_info_format(NDI_UNIQUE, 0, op,
"You paid %s for %s.",buf,query_name(op));
tmp=merge_ob(op,NULL);
if (pl->type == PLAYER && pl->contr->eric_server > 0) {
if (tmp) { /* it was merged */
esrv_del_item (pl->contr->eric_server, c);
op = tmp;
}
esrv_send_item(pl, op);
}
}
}
return 1;
}
int get_payment(object *pl) {
int ret;
D_LOCK(pl);
ret = get_payment2 (pl, pl->inv);
D_UNLOCK(pl);
if (pl->type == PLAYER && pl->contr->eric_server <= 0)
draw_inventory(pl);
return ret;
}
/* Modified function to give out platinum coins. This function is
* not as general as pay_for_item in finding money types - each
* new money type needs to be explicity code in here.
*/
void sell_item(object *op, object *pl) {
int i=query_cost(op,pl,F_SELL), count;
object *tmp;
archetype *at;
if(pl==NULL||pl->type!=PLAYER) {
LOG(llevDebug,"Object other than player tried to sell something.\n");
return;
}
if(!i) {
new_draw_info_format(NDI_UNIQUE, 0, pl,
"We're not interested in %s.",query_name(op));
/* Even if the character doesn't get anything for it, it may still be
* worth something. If so, make it unpaid
*/
if (op->value)
SET_FLAG(op, FLAG_UNPAID);
identify(op);
return;
}
D_LOCK(pl);
for (count=0; coins[count]!=NULL; count++) {
at = find_archetype(coins[count]);
if (at==NULL) LOG(llevError, "Could not find %s archetype", coins[count]);
else if ((i/at->clone.value)) {
tmp = get_object();
copy_object(&at->clone, tmp);
tmp->nrof = i/tmp->value;
i -= tmp->nrof * tmp->value;
tmp = insert_ob_in_ob(tmp, pl);
if(pl->contr->eric_server > 0)
esrv_send_item (pl, tmp);
}
}
if (i!=0)
LOG(llevError,"Warning - payment not zero: %d\n", i);
D_UNLOCK(pl);
new_draw_info_format(NDI_UNIQUE, 0, pl,
"You receive %s for %s.",query_cost_string(op,pl,1),
query_name(op));
SET_FLAG(op, FLAG_UNPAID);
identify(op);
}
/* SHOP_LISTINGS code added by Mark Wedel (master@cats.ucsc.edu)
*
* I put the code here, because it did seem shop related. I suppose it
* could also be put in the apply.c file.
*
* Various thoughts:
*
* Perhaps the location of the item relative to the menu sign should be given?
*
* Perhaps a better ordering/sorting of the objects could be done? (group
* all chain armor together, and then order by magic plus value, for
* example.)
*
* Tell the price of the item also?
*/
void shop_listing_more(object *op)
{
object *tmp,*old;
int lines = 7;
tmp = op->contr->menu->inv;
if (tmp == (object *) NULL) {
new_draw_info(NDI_UNIQUE, 0,op, "The Shop is currently empty.");
op->contr->state = ST_PLAYING; /* reset to normal state */
free_object(op->contr->menu);
op->contr->menu = NULL;
return;
}
/* If the person is using scroll, a blank line is probably better
* than clearing the window. Clearing the window is better for
* people that do not use scroll. Mark Wedel (master@rahul.net)
*/
if (op->contr->scroll)
new_draw_info(NDI_UNIQUE, 0,op, ""); /* add a blank line for readability */
else
clear_win_info(op); /* Seems more readable and faster to me. -Frank */
if (op->contr->state==ST_MENU_MORE)
new_draw_info(NDI_UNIQUE, 0,op, "The Shop contains (continued):");
else
new_draw_info(NDI_UNIQUE, 0,op, "The Shop contains:");
do {
/* mark object as being paid, so that '(unpaid)' isn't
added to the end -- By definition, an object being
viewed in the shop listing is unpaid.
*/
CLEAR_FLAG(tmp, FLAG_UNPAID);
/* Give a little more detail on some items.
* After all, telling the person that there is 5
* rings in the shop isn't especially useful.
*/
switch (tmp->type) {
case RING:
case AMULET:
case BRACERS:
case BOOTS:
case GLOVES:
case GIRDLE:
new_draw_info_format(NDI_UNIQUE, 0, op,
"%s %s",query_name(tmp),
describe_item(tmp));
break;
default:
new_draw_info(NDI_UNIQUE, 0,op, query_name(tmp));
break;
}
old = tmp;
tmp = tmp->below;
remove_ob(old);
free_object(old);
/* If we have put so many lines of inventory on the
* screen AND there is more to print, ask for them
* to enter a key.
*/
if (++lines>op->contr->infolines && tmp) {
new_draw_info(NDI_UNIQUE, 0,op,"Press any key to continue list");
op->contr->state = ST_MENU_MORE;
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,CS_QUERY_SINGLECHAR,"");
return; /* let the routine in player.c handle the rest */
}
} while (tmp);
free_object(op->contr->menu);
op->contr->menu = NULL;
op->contr->state = ST_PLAYING; /* reset to normal state */
}
void shop_listing(object *op)
{
int i,j;
char *map_mark = (char *) malloc(op->map->mapx * op->map->mapy);
object *tmp,*stack;
memset(map_mark, 0, op->map->mapx * op->map->mapy);
magic_mapping_mark(op, map_mark, 3);
if (op->contr->menu!=NULL) {
LOG(llevError,"Shop listing being called with non null menu point in player\n");
return;
}
op->contr->menu = get_object();
for (i=0; i<op->map->mapx; i++)
for (j=0; j<op->map->mapy; j++) {
if (map_mark[i + op->map->mapx * j]) {
stack =get_map_ob(op->map,i,j);
while (stack) {
if (QUERY_FLAG(stack, FLAG_UNPAID)) {
tmp = get_object();
copy_object(stack, tmp);
(void) insert_ob_in_ob(tmp, op->contr->menu);
}
stack = stack->above;
}
}
}
free(map_mark);
shop_listing_more(op);
}